package com.hk.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hk.api.outpainting.ImageOutPaintService;
import com.hk.api.outpainting.vo.CreateOutPaintingTaskRequest;
import com.hk.api.outpainting.vo.CreateOutPaintingTaskResponse;
import com.hk.api.outpainting.vo.GetOutPaintingTaskResponse;
import com.hk.auth.StpKit;
import com.hk.auth.service.SpaceUserAuthService;
import com.hk.common.utils.ColorUtil;
import com.hk.enums.PictureReviewStatusEnum;
import com.hk.enums.SpaceRoleEnum;
import com.hk.enums.UserRoleEnum;
import com.hk.exception.BusinessException;
import com.hk.exception.ErrorCode;
import com.hk.exception.ThrowExceptionUtil;
import com.hk.manager.CosManager;
import com.hk.manager.cache.LocalCacheManager;
import com.hk.manager.upload.FileUploadHandler;
import com.hk.manager.upload.URLUploadHandler;
import com.hk.mapper.PictureMapper;
import com.hk.model.entity.Picture;
import com.hk.model.entity.Space;
import com.hk.model.vo.IdVO;
import com.hk.model.vo.file.UploadFileVO;
import com.hk.model.vo.picture.*;
import com.hk.model.vo.space.SpaceVO;
import com.hk.model.vo.user.UserVO;
import com.hk.service.PictureService;
import com.hk.service.SpaceService;
import com.hk.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 20231
 * @description 针对表【picture(图片)】的数据库操作Service实现
 * @createDate 2024-12-16 19:08:13
 */
@Service
@Slf4j
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> implements PictureService {

    @Resource
    private FileUploadHandler fileUploadHandler;

    @Resource
    private URLUploadHandler urlUploadHandler;
    @Resource
    private UserService userService;
    @Resource
    private HttpServletRequest request;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private LocalCacheManager localCacheManager;
    @Resource
    private CosManager cosManager;
    @Resource
    private SpaceService spaceService;
    @Resource
    private ImageOutPaintService imageOutPaintService;
    @Resource
    private SpaceUserAuthService spaceUserAuthService;


    /**
     * @param inputSource 文件源
     * @param fileId      文件id
     * @param name        文件名
     * @param spaceId     空间id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PictureVO uploadPicture(Object inputSource, Long fileId, String name, Long spaceId) {
        UserVO userVO = userService.getCurrentUser(request);
        //校验参数
        ThrowExceptionUtil.throwIf(userVO == null, ErrorCode.NOT_LOGIN);

        //校验是否可以上传至该空间
        if (spaceId != null) {
            Space space = spaceService.getById(spaceId);
            ThrowExceptionUtil.throwIf(space == null, ErrorCode.ERROR_PARAM, "空间不存在");
//            ThrowExceptionUtil.throwIf(ObjectUtil.notEqual(userVO.getId(), space.getUserId()), ErrorCode.ERROR_PARAM, "无权限上传至该空间");
        }

        //如果是更新，判断图片是否存在
        if (fileId != null) {
            Picture picture = this.getById(fileId);
            ThrowExceptionUtil.throwIf(picture == null, ErrorCode.ERROR_PARAM, "文件不存在");
            //校验两个空间id是否一致
            ThrowExceptionUtil.throwIf(ObjectUtil.notEqual(picture.getSpaceId(), spaceId), ErrorCode.ERROR_PARAM, "上传空间错误");
        }
        UploadFileVO uploadFileVO = null;
        if (inputSource instanceof MultipartFile file) {
            uploadFileVO = fileUploadHandler.upload(file, userVO.getId().toString());
        } else {
            String fileUrl = (String) inputSource;
            uploadFileVO = urlUploadHandler.upload(fileUrl, userVO.getId().toString());
        }
        Picture picture = buildPicture(name, spaceId, uploadFileVO, userVO);
        fillReviewParam(picture, userVO, spaceId);
        String originUrl = null;
        if (fileId != null) {
            Picture originPicture = this.getById(fileId);
            originUrl = originPicture.getUrl();
            picture.setId(fileId);
            picture.setEditTime(new Date());
        }
        boolean result = this.saveOrUpdate(picture);
        ThrowExceptionUtil.throwIf(!result, ErrorCode.ERROR_SYSTEM, "图片上传失败");
        //更新空间大小
        if (spaceId != null) {
            Space space = spaceService.getById(spaceId);
            space.setTotalCount(space.getTotalCount() + 1);
            space.setTotalSize(space.getTotalSize() + uploadFileVO.getFileSize());
            spaceService.updateById(space);
        }
        //删除源文件
        if (originUrl != null) {
            log.error("删除源文件:{}", originUrl);
            cosManager.deleteObject(originUrl);
        }
        return converterToVO(picture);
    }


    /**
     * 填充审核参数
     *
     * @param picture
     * @param userVO
     * @param spaceId
     */
    private void fillReviewParam(Picture picture, UserVO userVO, Long spaceId) {
        if (spaceId == null) {
            //公共图库
            if (userVO.getRole() == UserRoleEnum.ADMIN.getCode()) {
                //管理员不用审核
                picture.setReviewStatus(PictureReviewStatusEnum.PASS.getCode());
            } else {
                picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getCode());
            }
        } else {
            //个人空间或团队空间
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getCode());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(IdVO idVO) {
        Picture picture = this.getById(idVO.getId());
        ThrowExceptionUtil.throwIf(picture == null, ErrorCode.ERROR_SYSTEM, "图片不存在");
        //校验权限 只有管理员和自己可以删除
//        UserVO userVO = userService.getCurrentUser(request);
//        ThrowExceptionUtil.throwIf(!picture.getUserId().equals(userVO.getId()) && UserRoleEnum.ADMIN.getCode() != userVO.getRole(), ErrorCode.ERROR_SYSTEM, "没有权限删除图片");
        this.removeById(idVO.getId());
        //删除图片占有空间
        if (picture.getSpaceId() != null && picture.getSpaceId() != 0) {
            Space space = spaceService.getById(picture.getSpaceId());
            space.setTotalSize(space.getTotalSize() - picture.getPicSize());
            space.setTotalCount(space.getTotalCount() - 1);
            spaceService.updateById(space);
        }
        //删除对象存储中的图片
        cosManager.deleteObject(picture.getUrl());
    }

    @Override
    public PictureVO updatePicture(PictureUpdateRequest updateRequest) {
        Picture picture = this.getById(updateRequest.getId());
        ThrowExceptionUtil.throwIf(picture == null, ErrorCode.ERROR_SYSTEM, "图片不存在");
        //校验权限 只有管理员和自己可以修改
        UserVO userVO = userService.getCurrentUser(request);
//        ThrowExceptionUtil.throwIf(!picture.getUserId().equals(userVO.getId()) && UserRoleEnum.ADMIN.getCode() != userVO.getRole(), ErrorCode.ERROR_SYSTEM, "没有权限编辑图片");
        fillReviewParam(picture, userVO, picture.getSpaceId());
        BeanUtil.copyProperties(updateRequest, picture);
        boolean update = this.updateById(picture);
        ThrowExceptionUtil.throwIf(!update, ErrorCode.ERROR_SYSTEM, "修改失败");
        return BeanUtil.copyProperties(picture, PictureVO.class);
    }

    @Override
    public List<PictureVO> getSpacePictureList(PictureQueryParamVO queryParamVO) {
        SpaceVO spaceVO = spaceService.getSpaceInfo(queryParamVO.getSpaceId());
        if (spaceVO == null) {
            return new ArrayList<>();
        }
        String name = queryParamVO.getName();
        String category = queryParamVO.getCategory();
        String introduction = queryParamVO.getIntroduction();
        List<String> tags = queryParamVO.getTags();
        LambdaQueryWrapper<Picture> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Picture::getSpaceId, spaceVO.getId());
        queryWrapper.eq(StrUtil.isNotBlank(category), Picture::getCategory, category);
        queryWrapper.like(StrUtil.isNotBlank(name), Picture::getName, name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), Picture::getIntroduction, introduction);
        List<Picture> pictures = this.list(queryWrapper);
        pictures = filterByTag(tags, pictures);
        if (StrUtil.isNotBlank(queryParamVO.getPicColor())) {
            Color targetColor = Color.decode(queryParamVO.getPicColor());
            pictures = pictures.stream().filter(picture -> StrUtil.isNotBlank(picture.getPicColor())).sorted(Comparator.comparingDouble(picture -> {
                String picColor = picture.getPicColor();
                Color pictureColor = Color.decode(picColor);
                return -ColorUtil.calculateSimilarity(targetColor, pictureColor);
            })).collect(Collectors.toList());
        }
        return converterToVO(pictures);
    }


    @Override
    public PictureVO getPictureInfo(Long id) {
        Picture picture = this.getById(id);
        ThrowExceptionUtil.throwIf(picture == null, ErrorCode.ERROR_PARAM, "图片不存在");
        PictureVO pictureVO = converterToVO(picture);
        String loginId = (String) StpKit.SPACE.getLoginId();
        UserVO userVO = userService.getUserInfo(Long.valueOf(loginId));
        if (picture.getSpaceId() != null && picture.getSpaceId() != 0) {
            Space space = spaceService.getById(picture.getSpaceId());
            List<String> permissionList = spaceUserAuthService.getPermissionList(space, userVO);
            pictureVO.setPermissionList(permissionList);
        } else {
            if (userVO.getRole() == UserRoleEnum.ADMIN.getCode()) {
                pictureVO.setPermissionList(spaceUserAuthService.getPermissionsByRole(SpaceRoleEnum.ADMIN.getValue()));
            } else {
                pictureVO.setPermissionList(spaceUserAuthService.getPermissionsByRole(SpaceRoleEnum.VIEWER.getValue()));
            }
        }
        return pictureVO;
    }

    @Override
    public Page<PictureVO> getPicturePageList(PictureQueryParamVO queryParamVO) {
        String name = queryParamVO.getName();
        String category = queryParamVO.getCategory();
        String introduction = queryParamVO.getIntroduction();
        List<String> tags = queryParamVO.getTags();
        LambdaQueryWrapper<Picture> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StrUtil.isNotBlank(category), Picture::getCategory, category);
        queryWrapper.eq(queryParamVO.getReviewStatus() != null, Picture::getReviewStatus, queryParamVO.getReviewStatus());
        queryWrapper.like(StrUtil.isNotBlank(name), Picture::getName, name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), Picture::getIntroduction, introduction);
        queryWrapper.orderByDesc(Picture::getEditTime);
        if (CollectionUtil.isNotEmpty(tags)) {
            tags.forEach(tag -> queryWrapper.like(Picture::getTags, tag));
        }
        Page<Picture> page = this.page(new Page<>(queryParamVO.getCurrent(), queryParamVO.getPageSize()), queryWrapper);
        Page<PictureVO> picturePage = new Page<>(queryParamVO.getCurrent(), queryParamVO.getPageSize(), page.getTotal());
//        List<PictureVO> pictureVOList = page.getRecords().stream().map(this::converterToVO).toList();
        List<PictureVO> pictureVOList = converterToVO(page.getRecords());
        picturePage.setRecords(pictureVOList);
        buildUserVO(pictureVOList);
        return picturePage;
    }

    @Override
    public Page<PictureVO> getPicturePage(PictureQueryParamVO queryParamVO) {

//        String jsonStr = JSONUtil.toJsonStr(queryParamVO);
//        String key = DigestUtil.md5Hex(jsonStr);
        //从本地缓存中获取
//        String cacheValue = localCacheManager.get(key);
//        if (cacheValue != null) {
//            return JSONUtil.toBean(cacheValue, Page.class);
//        }
        //从redis缓存中获取
//        String redisKey = Constant.CACHE_PREFIX + key;
//        cacheValue = stringRedisTemplate.opsForValue().get(redisKey);
//        if (cacheValue != null) {
//            return JSONUtil.toBean(cacheValue, Page.class);
//        }
        //从数据库中获取
        String name = queryParamVO.getName();
        String category = queryParamVO.getCategory();
        String introduction = queryParamVO.getIntroduction();
        List<String> tags = queryParamVO.getTags();
        LambdaQueryWrapper<Picture> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StrUtil.isNotBlank(category), Picture::getCategory, category);
        queryWrapper.like(StrUtil.isNotBlank(name), Picture::getName, name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), Picture::getIntroduction, introduction);
        queryWrapper.eq(Picture::getReviewStatus, PictureReviewStatusEnum.PASS.getCode());
//        queryWrapper.isNull(Picture::getSpaceId);
        queryWrapper.orderByDesc(Picture::getEditTime);
        if (CollectionUtil.isNotEmpty(tags)) {
            tags.forEach(tag -> queryWrapper.like(Picture::getTags, tag));
        }
        Page<Picture> page = this.page(new Page<>(queryParamVO.getCurrent(), queryParamVO.getPageSize()), queryWrapper);
        Page<PictureVO> picturePage = new Page<>(queryParamVO.getCurrent(), queryParamVO.getPageSize(), page.getTotal());
//        List<PictureVO> pictureVOList = page.getRecords().stream().map(this::converterToVO).toList();
        List<PictureVO> pictureVOList = converterToVO(page.getRecords());
        picturePage.setRecords(pictureVOList);
        buildUserVO(pictureVOList);
        String value = JSONUtil.toJsonStr(picturePage);
        //存储到缓存
//        long time = 300 + RandomUtil.randomInt(0, 300);
//        stringRedisTemplate.opsForValue().set(redisKey, value, time, TimeUnit.SECONDS);
//        localCacheManager.put(key, value);
        return picturePage;
    }

    /**
     * 审核图片
     *
     * @param reviewParamVO
     * @return
     */
    @Override
    public boolean pictureReview(PictureReviewParamVO reviewParamVO) {
        Long id = reviewParamVO.getId();
        Integer reviewStatus = reviewParamVO.getReviewStatus();
        String reviewMessage = reviewParamVO.getReviewMessage();
        ThrowExceptionUtil.throwIf(id == null || reviewStatus == null, ErrorCode.ERROR_PARAM);
        Picture picture = this.getById(id);
        ThrowExceptionUtil.throwIf(picture == null, ErrorCode.ERROR_PARAM);
        UserVO userVO = userService.getCurrentUser(request);
        Picture update = new Picture();
        update.setId(id);
        update.setReviewerId(userVO.getId());
        update.setReviewStatus(reviewStatus);
        update.setReviewMessage(reviewMessage);
        update.setReviewTime(new Date());
        return this.updateById(update);

    }

    @Override
    public Integer grabPicture(String searchText, Integer count, String namePrefix) {
        String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&mmasync=1", searchText);
        Document document;
        try {
            document = Jsoup.connect(fetchUrl).get();
        } catch (IOException e) {
            log.error("抓取图片失败", e);
            throw new BusinessException(ErrorCode.ERROR_SYSTEM, "抓取图片失败");
        }
        Element div = document.getElementsByClass("dgControl").first();
        if (div == null) {
            throw new BusinessException(ErrorCode.ERROR_SYSTEM, "抓取图片失败");
        }
        Elements imgElementList = div.select("img.mimg");
        int grabCount = 1;
        for (Element imgElement : imgElementList) {
            String imgUrl = imgElement.attr("src");
            if (StrUtil.isBlank(imgUrl)) {
                log.error("当前链接下载失败，{}", imgUrl);
                continue;
            }
            //处理图片带有特殊字符
            int questionMarkIndex = imgUrl.indexOf("?");
            if (questionMarkIndex > -1) {
                imgUrl = imgUrl.substring(0, questionMarkIndex);
            }
            try {
                PictureVO pictureVO = this.uploadPicture(imgUrl, null, namePrefix + grabCount, null);
            } catch (Exception e) {
                log.error("当前链接下载失败,错误原因：", e);
                continue;
            }
            grabCount++;
            if (grabCount >= count) {
                break;
            }
        }
        return grabCount;
    }

    @Override
    public CreateOutPaintingTaskResponse createOutPaintTask(PictureOutPaintingVO outPaintingVO) {
        String pictureId = outPaintingVO.getPictureId();
        Picture picture = getById(pictureId);
        ThrowExceptionUtil.throwIf(picture == null, ErrorCode.ERROR_PARAM, "图片不存在");
        CreateOutPaintingTaskRequest taskRequest = new CreateOutPaintingTaskRequest();
        CreateOutPaintingTaskRequest.Input input = new CreateOutPaintingTaskRequest.Input();
        input.setImageUrl(picture.getUrl());
        taskRequest.setInput(input);
        BeanUtil.copyProperties(outPaintingVO, taskRequest);
        //构建请求参数
        return imageOutPaintService.createTask(taskRequest);
    }

    @Override
    public GetOutPaintingTaskResponse getOutPaintTask(String taskId) {
        return imageOutPaintService.getTaskResponse(taskId);
    }

    private void buildUserVO(List<PictureVO> pictureVOList) {
        if (CollectionUtil.isEmpty(pictureVOList)) return;
        List<Long> userIdList = pictureVOList.stream().map(PictureVO::getUserId).distinct().toList();
        List<UserVO> userList = userService.getUserListById(userIdList);
        if (CollectionUtil.isEmpty(userList)) return;
        Map<Long, UserVO> userMap = userList.stream().collect(Collectors.toMap(UserVO::getId, Function.identity()));
        pictureVOList.forEach(pictureVO -> pictureVO.setUserVO(userMap.get(pictureVO.getUserId())));
    }

    /**
     * 将实体类转换为VO
     *
     * @param picture
     * @return
     */
    public PictureVO converterToVO(Picture picture) {
        if (picture == null) return null;
        PictureVO pictureVO = new PictureVO();
        BeanUtil.copyProperties(picture, pictureVO);
        pictureVO.setTagsList(JSONUtil.toList(picture.getTags(), String.class));
        pictureVO.setPicColor(ColorUtil.convertColor(picture.getPicColor()));
        return pictureVO;
    }

    private List<PictureVO> converterToVO(List<Picture> pictures) {
        if (CollectionUtil.isEmpty(pictures)) return new ArrayList<>();
        return pictures.stream().map(this::converterToVO).toList();
    }

    /**
     * 将VO转换为实体类
     *
     * @param pictureVO
     * @return
     */
    public Picture converterToObj(PictureVO pictureVO) {
        if (pictureVO == null) return null;
        Picture picture = new Picture();
        BeanUtil.copyProperties(pictureVO, picture);
        picture.setTags(JSONUtil.toJsonStr(pictureVO.getTagsList()));
        return picture;
    }

    /**
     * 根据标签过滤
     *
     * @param tags 标签
     */
    private List<Picture> filterByTag(List<String> tags, List<Picture> pictures) {
        if (CollectionUtil.isNotEmpty(tags) && CollectionUtil.isNotEmpty(pictures)) {
            pictures = pictures.stream().filter(picture -> {
                List<String> list = JSONUtil.toList(picture.getTags(), String.class);
                return CollectionUtil.containsAny(list, tags);
            }).collect(Collectors.toList());
        }
        return pictures;
    }

    private static Picture buildPicture(String name, Long spaceId, UploadFileVO uploadFileVO, UserVO userVO) {
        Picture picture = new Picture();
        picture.setSpaceId(spaceId);
        picture.setUrl(uploadFileVO.getUrl());
        picture.setUserId(userVO.getId());
        picture.setName(StrUtil.isNotBlank(name) ? name : uploadFileVO.getFileName());
        picture.setIntroduction(uploadFileVO.getIntroduction());
        picture.setCategory(uploadFileVO.getCategory());
        picture.setTags(JSONUtil.toJsonStr(uploadFileVO.getTagsList()));
        picture.setPicSize(uploadFileVO.getFileSize());
        picture.setPicWidth(uploadFileVO.getPicWidth());
        picture.setPicHeight(uploadFileVO.getPicHeight());
        picture.setPicScale(uploadFileVO.getPicScale());
        picture.setPicFormat(uploadFileVO.getPicFormat());
        picture.setPicColor(uploadFileVO.getPicColor());
        return picture;
    }
}




